![]() |
![]() |
|
Eine Beschreibung der genannten Klassen können Sie der Tabelle 9.3 entnehmen.
Das sind noch nicht alle vordefinierten Listener, die das .NET Framework anbietet. Die anderen lassen sich aber schwerlich in einem .NET-Grundlagenteil einbauen, deshalb soll es auch bei der kurzen Erwähnung bleiben. Wenn Ihnen danach ist, können Sie auch eigene Listener definieren. Sie brauchen dazu nur die Klasse TraceListener abzuleiten. Die Listener-Objekte sind an die Klassen Debug und Trace gebunden. Beide Klassen stellen mit Listeners eine Eigenschaft vom Typ TraceListenerCollection bereit. Diese Auflistung weist die von einer ArrayList bekannten Eigenschaften und Methoden auf, also z. B. Add, Remove, Clear. Beachten Sie, dass die Listener-Objekte sowohl von Trace als auch von Debug gemeinsam benutzt werden. Beide Klassen bedienen sich also derselben Auflistung. Sehen wir uns nun an einem Beispiel an, wie die Listener eingesetzt werden.
In der ersten Anweisung wird die Listeners-Auflistung mit Clear geleert. Weil damit der standardmäßige Eintrag des DefaultTraceListeners-Objekts gelöscht wird, würde ein Debug.Write- oder Trace.Write-Methodenaufruf keinen Abnehmer mehr finden und im Nirwana verpuffen. In der folgenden Codezeile wird daher die Klasse ConsoleTraceListener instanziiert. Dieses Objekt leitet die Ausgabemeldungen an die Konsole um und wird mit der darauf folgenden Anweisung der TraceListenerCollection übergeben. Da sich die Klassen Trace und Debug die installierten Listener gleichberechtigt teilen, werden in der Build-Konfiguration Debug beide Meldungen an der Konsole angezeigt. Die Klasse »TextWriterTraceListener«Eine Liste von insgesamt sieben Konstruktoren zeugt davon, dass die Klasse TextWriterTraceListener die flexibelste der Listener ist. Neben dem parameterlosen Konstruktor kann dieser Listener auch mit einem TextWriter- oder einem Stream-Objekt verbunden werden.
Nehmen wir an, dass Sie Ihre Debug-Informationen in einer Datei speichern möchten. Im einfachsten Fall brauchen Sie dem Konstruktor nur eine Zeichenfolge zu übergeben, die den Pfad zu der Datei beschreibt. Anschließend muss der neue Listener registriert werden.
TextWriterTraceListener schreibt alle Ausgabeinformationen zunächst in einen Puffer. Mit dem Aufruf der Methode Flush wird der Puffer geleert und der Inhalt in die Datei geschrieben. Alternativ dazu können Sie auch die Eigenschaft AutoFlush=True setzen. Mit Close wird das TextWriterTraceListener-Objekt geschlossen. Soll danach die Ablaufverfolgung wieder aufgenommen werden, muss der Listener neu initialisiert und registriert werden. Die Klassen Debug und Trace bedienen sich derselben Listener. Daher werden beide Write-Anweisungen in der Datei einen Abnehmer finden – vorausgesetzt natürlich, dass die Anwendung im Debug- und nicht im Release-Modus ausgeführt wird. Ist die Datei bereits vorhanden, werden die Debug-Informationen an den alten Dateiinhalt angehängt. Vielleicht wünschen Sie aber, dass die alten Einträge überschrieben werden. Hier hilft die Überladung des TextWriterTraceListener-Konstruktors weiter, welche die Referenz auf ein Stream-Objekt erwartet. Im folgenden Beispiel wird dem Konstruktor eine Referenz vom Typ FileStream übergeben. Das FileStream-Objekt leitet die Ausgabe in eine Datei um. Ist die Datei bereits vorhanden, wird die alte überschrieben, ansonsten wird eine neue erzeugt. Dafür sorgt die Angabe FileMode.Create.
Sehr häufig werden Listener dazu benutzt, aufgetretene Laufzeitfehler im Catch-Zweig der Fehlerbehandlung zu protokollieren. In diesem Beispiel wird in der Methode Operation eine Exception mit einer spezifischen Fehlermeldung erzeugt. Neben der Fehlermeldung werden in der Protokolldatei auch noch Datum und Uhrzeit des Auftretens der Ausnahme festgehalten. Die entsprechende Information liefert uns die statische Methode Now der Klasse DateTime. Mehrere Listener verwaltenEin TextWriterTraceListener beschreibt immer genau einen Informationsabnehmer und eignet sich nicht nur dazu, Fehler zu protokollieren. Sie können einen Listener auch genauso gut dazu benutzen, allgemeine Informationen in einen Stream zu schreiben. Es sind deshalb auch Szenarien vorstellbar, in denen mehrere Listener mit unterschiedlichen Aufgaben benötigt werden. Grundsätzlich werden Meldungen von jedem registrierten Listener gepuffert. Soll eine Meldung nur von einem bestimmter Listener verarbeitet werden, muss die Registrierung der anderen Listener zumindest zeitweise aufgehoben werden. Mit den Methoden Add, Remove sowie dem Indexer ist das grundsätzlich möglich. Allerdings ist es einfacher, ein Listenelement über einen Namen als über den Index in der Auflistung anzusprechen. Daher stellt die Klasse TextWriterTraceListener Konstruktoren mit einem zweiten Parameter bereit, um den Listener zu benennen. Der Name des Listeners wird in der Eigenschaft Name eingetragen. Im folgenden Beispiel werden zwei Listener registriert und beiden eine Zeichenfolge übergeben. Anschließend wird ein Listener aus der TraceListenerCollection gelöscht. Die anschließende Meldung wird nur vom verbleibenden Listener weitergeleitet.
Sollten Sie versuchen, den Informationsfluss nur über den Aufruf der Flush-Methode auf die einzelnen Listener zu steuern, werden Sie damit wenig Erfolg haben. Denn solange ein Listener registriert ist, werden alle eingehenden Informationen in seinen Puffer geschrieben und beim nächsten Aufruf von Flush an den Abnehmer weitergeleitet. Die Klasse »EventLogTraceListener«Alternativ zum TextWriterTraceListener können Sie mit einer Instanz der Klasse EventLogTraceListener Meldungen in das Windows-Ereignisprotokoll schreiben. Auch dieser Listener muss in der Listeners-Auflistung mit der Add-Methode registriert werden. Das Verhalten hinsichtlich der Pufferung der Meldungen ist identisch mit der, die wir im letzten Abschnitt erörtert haben. Die Klasse EventLogTraceListener stellt drei Konstruktoren bereit:
Rufen Sie den parameterlosen Konstruktor auf, müssen alle Eigenschaften konfiguriert werden, bevor Nachrichten an ein Ereignisprotokoll gesendet werden können. Geeigneter erscheint daher der Konstruktor, der eine Zeichenfolge erwartet, die das Ereignisprotokoll namentlich beschreibt. Diagnosemeldungen, die von den Klassen Debug oder Trace geschrieben werden, werden Sie anschließend im Anwendungsprotokoll von Windows wiederfinden.
Mit der dritten Überladung besteht die Möglichkeit, eine Ablaufverfolgungs- oder Debug-Meldung an ein beliebiges Ereignisprotokoll auf der lokalen oder einer anderen Maschine weiterzuleiten. Darüber hinaus können Sie auch ein benutzerdefiniertes Ereignisprotokoll erstellen. Der Konstruktor erwartet dann die Referenz auf ein EventLog-Objekt, mit dem ein Ereignisprotokoll von Windows beschrieben wird. Im folgenden Codefragment wird ein Ereignisprotokoll namens MyProtocol auf der lokalen Maschine eingerichtet. Die lokale Maschine wird durch eine Zeichenfolge, die nur einen Punkt enthält, beschrieben. Sie könnten aber auch den Namen eines entfernten Rechners angeben.
Mit der Eigenschaft Source geben Sie einen Bezeichner für die Ereignisquelle an. Innerhalb der Ereignisprotokolle von Windows muss dieser eindeutig sein. Wollen Sie in Erfahrung bringen, welche Ereignisprotokolle auf der lokalen Maschine eingerichtet sind, können Sie sich über den Aufruf der statischen Methode GetEventLogs der Klasse EventLog ein Array gleichen Typs besorgen und auf jedem Element die Log-Eigenschaft auswerten, die den Namen des Ereignisprotokolls als Zeichenfolge liefert.
Sind keine benutzerdefinierten Ereignisprotokolle in der Registrierungsdatenbank eingetragen, sollte die Ausgabe Application, System und Security lauten. Grundsätzlich sollten Sie sich darüber bewusst sein, dass das Ereignisprotokoll nicht dazu dient, eine große Anzahl allgemeiner Meldungen vorzuhalten. Beschränken Sie daher den Einsatz des EventLogTraceListeners auf wirklich wichtige Meldungen. 9.2.5 Steuerung der Protokollierung mit Schaltern
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Dim mySwitch As BooleanSwitch = _ |
| New BooleanSwitch("MeinSchalter", "In MyApplication") |
Standardmäßig sind alle Ausgaben zunächst deaktiviert. Sollen die Ausgaben an die Listener weitergeleitet werden, müssen Sie die Eigenschaft Enabled des BooleanSwitch-Objekts auf True setzen:
| mySwitch.Enabled = True |
Um die aktuelle Schalterstellung auszuwerten und den weiteren Protokollierungsablauf zu steuern, bieten sich die beiden Methoden WriteIf und WriteLineIf der Klassen Debug und Trace an, z. B.:
| Trace.WriteLineIf(mySwitch.Enabled, "Ablaufverfolgung:" + _ |
| DateTime.Now.ToString()); |
Nur dann, wenn der Schalter mit Enabled=True aktiviert ist, wird die Meldung mit genauer Datums- und Zeitangabe an alle registrierten Listener geschickt.
Damit sind aber noch nicht alle Möglichkeiten erschöpft. .NET-Anwendungen können um eine optionale Anwendungskonfigurationsdatei erweitert werden, in der nach Bedarf Einstellungen verändert werden können, ohne dass die Anwendung neu kompiliert werden muss. Anwendungskonfigurationsdateien sind XML-Dateien und immer im Stammverzeichnis der Anwendung zu finden. Der Name einer Konfigurationsdatei setzt sich aus dem Namen der Anwendungsdatei und dem Suffix .config zusammen. Lautet die Anwendungsdatei MyApplication.exe, heißt die Konfigurationsdatei demnach MyApplication.exe.config.
Mit den Vorgaben in einer Konfigurationsdatei können Sie jederzeit das Laufzeitverhalten einer Anwendung beeinflussen und steuern, denn eine Konfigurationsdatei wird vor dem Start einer Anwendung ausgewertet – vorausgesetzt, der Anwendung wurde eine Konfigurationsdatei zugeordnet.
Um eine Anwendungskonfigurationsdatei hinzuzufügen, öffnen Sie im Projektmappen-Explorer das Kontextmenü des Projekts und wählen Hinzufügen Neues Element... In der Liste markieren Sie Anwendungskonfigurationsdatei. Nach der Bestätigung wird die neue Komponente der Anwendung mit dem Dateinamen App.config hinzugefügt und im Codeeditor geöffnet. Nachfolgend sehen Sie die vom Visual Studio bereitgestellte Struktur, die bereits die für die Steuerung der Protokollierung mittels Schalter wichtigsten XML-Elemente enthält. Der besseren Übersicht wegen habe ich allerdings auf die ansonsten auch noch vorhandenen Kommentare verzichtet.
| <?xml version="1.0" encoding="utf-8" ?> |
| <configuration> |
| <system.diagnostics> |
| <sources> |
| <!-- Dieser Abschnitt definiert die Protokollierungskonfiguration für My.Application.Log --> |
| <source name="DefaultSource" switchName="DefaultSwitch"> |
| <listeners> |
| <add name="FileLog"/> |
| <!--<add name="EventLog"/>--> |
| </listeners> |
| </source> |
| </sources> |
| <switches> |
| <add name="DefaultSwitch" value="Information" /> |
| </switches> |
| <sharedListeners> |
| <add name="FileLog" type="Microsoft.VisualBasic.Logging.FileLogTraceListener, Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" |
| initializeData="FileLogWriter"/> |
| <!--<add name="EventLog" type="System.Diagnostics.EventLogTraceListener" initializeData="APPLICATION_NAME"/> --> |
| </sharedListeners> |
| </system.diagnostics> |
| </configuration> |
In der Anwendungskonfigurationsdatei werden nicht nur die Schalterstellungen angegeben, sondern darüber hinaus kann das Laufzeitverhalten der gesamten Anwendung maßgeblich beeinflusst werden. In Kapitel 27 werden Sie noch weitere Details zu den Konfigurationsdateien erfahren.
Anstatt ein BooleanSwitch-Objekt mit einer Anweisung im Code zu aktivieren bzw. zu deaktivieren, lässt sich das Verhalten des Schalters in der Anwendungskonfigurationsdatei festlegen. Dazu müssen Sie diese folgendermaßen ändern bzw. ergänzen:
| <?xml version="1.0" encoding="utf-8" ?> |
| <configuration> |
| <system.diagnostics> |
| <switches> |
| <add name="MeinSchalter" value="1" /> |
| </switches> |
| </system.diagnostics> |
| ... |
| </configuration> |
Innerhalb des switches-Knotens wird mit dem add-Element der entsprechende Schalter festgelegt. Eine Konfigurationsdatei ist nicht nur auf die Steuerung eines Schalters beschränkt. Wollen Sie die Ausgabe über mehrere Schalter steuern, fügen Sie einfach für jeden Schalter ein weiteres add-Element hinzu. Das Attribut name gibt den Namen des Schalters an, das Attribut value die Schalterstellung. Dabei steht 1 für die Aktivierung und 0 für die Deaktivierung des Schalters.
In unserem Beispiel ist der Schalter aktiviert. Deshalb wird die Anwendung auch alle Ausgabemeldungen an die registrierten Listener weiterleiten. Wollen wir die Ausgabemeldungen nicht mehr protokollieren, brauchen wir die Konfigurationsdatei nur mit dem Editor zu öffnen und value auf den Wert 0 zu setzen. Beim nächsten Start der Anwendung ist damit die Protokollierung abgeschaltet.
Beachten Sie, dass die Angabe der Schalterstellung im Programmcode dazu führt, dass die Vorgaben in der Konfigurationsdatei verworfen werden.
Im Gegensatz zu einem BooleanSwitch-Objekt, das nur die Einstellungen aktiviert und deaktiviert zulässt, können die Ablaufverfolgungs- und Debug-Ausgaben mit einem TraceSwitch-Objekt über die Festlegung mehrerer Ebenen gesteuert werden. Verantwortlich für die Steuerung der Ablaufverfolgung ist die Eigenschaft Level, die vom Typ der Enumeration TraceLevel ist.
| Public Property Level As TraceLevel |
In TraceLevel werden Ablaufverfolgungsebenen definiert, die Sie der folgenden Tabelle entnehmen können.
| Member | Wert | Beschreibung |
| Off | 0 | Es werden keine Ausgaben an die Listener weitergegeben. |
| Error | 1 | Es werden Ausgaben von Fehlermeldungen an die Listener weitergeleitet. |
| Warning | 2 | Es werden Ausgaben von Fehler- und Warnmeldungen an die Listener weitergeleitet. |
| Info | 3 | Es werden Ausgaben von Fehler-, Warn- und Informationsmeldungen an die Listener weitergeleitet. |
| Verbose | 4 | Es werden alle Meldungen an die Listener weitergeleitet. |
Wollen Sie ein TraceSwitch-Objekt erzeugen, müssen Sie dem Konstruktor den Namen des Schalters und eine Beschreibung übergeben. Damit gleicht die Instanziierung der von BooleanSwitch. Um die Ablaufverfolgungsebene festzulegen, weisen Sie der Eigenschaft Level einen Wert zu, z. B.:
| Dim mySwitch As TraceSwitch = _ |
| New TraceSwitch("MeinSchalter", "In MyApplication") |
| mySwitch.Level = TraceLevel.Warning |
Damit in Abhängigkeit von der Ablaufverfolgungsebene mit den Methoden WriteIf und WriteLineIf ein boolescher Wert ausgewertet werden kann, sind in der Klasse TraceSwitch die Eigenschaften
| TraceError |
| TraceWarning |
| TraceInfo |
| TraceVerbose |
definiert, die je nach Schalterstellung True oder False zurückgeben. Um die Ausgabe der Meldungen zu steuern, müssen Sie in den Methoden WriteIf oder WriteLineIf nur eine der Eigenschaften überprüfen:
| Trace.WriteLineIf(mySwitch.TraceWarning, _ |
| "Ablaufverfolgung:" + DateTime.Now.ToString()) |
Die Einstellung der Eigenschaften Level umfasst gleichzeitig auch die niedriger bewerteten Einstellungen, ausschließlich TraceLevel.Off. Wenn im Code beispielsweise die Ablaufverfolgungsebene auf TraceLevel.Info festgelegt ist, bedeutet dies, dass die Eigenschaften TraceError, TraceWarning und TraceInfo True liefern, während TraceVerbose False ist.
Zur Steuerung des oder der TraceSwitch-Objekte bietet sich ebenfalls die Anwendungskonfigurationsdatei an. Dem Attribut value müssen Sie für eine bestimmte Ablaufverfolgungs- ebene einen zugeordneten Wert übergeben. Sie können diesen der Tabelle 9.4 entnehmen. Die folgende Konfigurationsdatei beschreibt die Einstellung TraceLevel.Info.
| <?xml version="1.0" encoding="utf-8" ?> |
| <configuration> |
| <system.diagnostics> |
| <switches> |
| <add name="MeinSchalter" value="3" /> |
| </switches> |
| </system.diagnostics> |
| ... |
| </configuration> |
Die bedingte Kompilierung ermöglicht es, Codeabschnitte oder Methoden nur dann zu kompilieren, wenn ein bestimmtes Symbol definiert ist. Üblicherweise werden bedingte Codeabschnitte dazu benutzt, während der Entwicklungsphase den Zustand der Anwendung zur Laufzeit zu testen. Bevor ein Release-Build der Anwendung erstellt wird, wird das Symbol entfernt. Die Abschnitte, deren Code als bedingt kompilierbar gekennzeichnet ist, werden dann nicht mitkompiliert.
Der folgende Code zeigt ein Beispiel für bedingte Kompilierung:
| #Const TEST = 10 |
| Module Module1 |
| Sub Main() |
| #If TEST = 10 Then |
| Console.WriteLine("TEST = 10") |
| #Else |
| Console.WriteLine("TEST <> 10") |
| #End If |
| Console.ReadLine() |
| End Sub |
Mit der Prägprozessordirektiven #dConst wird das Symbol TEST definiert und diesem der Wert 10 zugewiesen. Symbole werden immer vor der ersten Anweisung, die selbst keine #Const-Direktive ist, festgelegt. Eine so beschrieben Direktive gilt nur in der Quellcodedatei, in der sie definiert ist.
Mit #If, #ElseIf oder #Else wird der Wert oder das Vorhandensein des angegebenen Symbols getestet. Stimmt die Bedingung mit der Festlegung des Symbols überein, liefert die Prüfung das Ergebnis True, und der Code wird ausgeführt. Da im Beispielcode das Symbol TEST mit dem Wert 10 definiert ist, wird die Ausgabe lauten:
| TEST = 10 |
Standardmäßig sind in VB-Projekten die beiden Symbole DEBUG und TRACE vordefiniert. Diese Vorgabe ist im Projekteigenschaftsfenster eingetragen und hat anwendungsweite Gültigkeit. Die Symbole beschreiben durch ihre Existenz den booleschen Wert True. Sie können sie löschen oder auch weitere hinzufügen. Öffnen Sie dazu das Eigenschaftsfenster des Projekts, markieren Sie die Lasche Kompilieren, und klicken Sie auf die Schaltfläche Erweiterte Kompilierungsfunktion. Es öffnet sich ein Dialog wie in Abbildung 9.6 gezeigt.

Hier klicken, um das Bild zu Vergrößern
Abbildung 9.6 Dialog zur Festlegung der Symbole der bedingten Kompilierung
Das Projekteigenschaftsfenster bietet darüber hinaus den Vorteil, dass sich die Symbole einer bestimmten Build-Konfiguration zuordnen lassen. Wählen Sie in der Drop-down-Liste der Karte Kompilieren im Eigenschaftsfenster Konfiguration die Build-Konfiguration aus, für welche die angegebenen Symbole gültig sein sollen. Wenn Sie beispielsweise keine Direktiven im Code angeben, dafür aber der Debug-Konfiguration das Symbol TEST zugeordnet haben, wird der in #If – #End If eingeschlossene Code im Debug-Build mitkompiliert, im Release-Build jedoch nicht. Die im Projekteigenschaftsfenster definierten Direktiven gelten projektweit.
| << zurück |
|
||||||||||||||
|
||||||||||||||
|
||||||||||||||
|
||||||||||||||
Copyright © Galileo Press 2007
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken.
Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die
gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich
geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung,
Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.